package cn.com.bluemoon.daps.model.service.impl;

import cn.com.bluemoon.daps.common.enums.BmStatus;
import cn.com.bluemoon.daps.common.enums.DatabaseType;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.common.toolkit.TreeUtils;
import cn.com.bluemoon.daps.model.entity.DapDataModel;
import cn.com.bluemoon.daps.model.entity.DapDataModelGroup;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.model.mapper.DapDataModelGroupMapper;
import cn.com.bluemoon.daps.model.service.DapDataModelGroupService;
import cn.com.bluemoon.daps.model.service.DapDataModelService;
import cn.com.bluemoon.daps.model.service.DapDataModelTableService;
import cn.com.bluemoon.daps.model.vo.group.ModelSimpleTableVo;
import cn.com.bluemoon.daps.model.vo.group.in.GroupTableSearchVo;
import cn.com.bluemoon.daps.model.vo.group.out.*;
import cn.com.bluemoon.daps.model.vo.group.out.DapDataModelGroupDto.DapModelGroupTableDto;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasource;
import cn.com.bluemoon.daps.system.entity.DapSystemInfo;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.util.function.Tuple2;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

/**
 * <p>
 * 模型分组表 服务实现类
 * </p>
 *
 * @author admin
 * @since 2021-02-02
 */
@Slf4j
@Service
public class DapDataModelGroupServiceImpl extends ServiceImpl<DapDataModelGroupMapper, DapDataModelGroup>
        implements DapDataModelGroupService {
    private static final String GROUP_AND_MODEL_SPLIT_CHAR = ":";
    /**
     * 本地缓存modelid+groupName的组信息缓存，带超时ttl
     */
    private static final Cache<String, List<DapDataModelGroup>> GROUP_CACHE_BY_NAME_AND_MODEL = CacheBuilder.newBuilder()
            .initialCapacity(20)
            .refreshAfterWrite(1, TimeUnit.MINUTES)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .build(new CacheLoader<String, List<DapDataModelGroup>>() {
                @Override
                public List<DapDataModelGroup> load(String groupAndModelId) throws Exception {
                    if (!groupAndModelId.contains(GROUP_AND_MODEL_SPLIT_CHAR))
                        return Collections.emptyList();
                    String[] groupAndModelIdArr = groupAndModelId.split(GROUP_AND_MODEL_SPLIT_CHAR);
                    DapDataModelGroupService groupService = SpringUtil.getBean(DapDataModelGroupService.class);
                    LambdaQueryWrapper<DapDataModelGroup> eq = Wrappers.<DapDataModelGroup>lambdaQuery()
                            .eq(DapDataModelGroup::getName, groupAndModelIdArr[0])
                            .eq(DapDataModelGroup::getModelId, groupAndModelIdArr[1]);
                    return groupService.list(eq);
                }
            });
    @Resource
    private DapDataModelService modelService;
    @Resource
    private DapSystemModuleFacade systemModuleFacade;
    @Resource
    private DapDataModelTableService tableService;

    /**
     * 初始化模型创建时候的分组基础信息，用于在新建模型中进行获取
     * <p>
     * isDwSystem 是否为数仓模型、对应数仓模型的分组和非数仓分组
     *
     * @param modelId 模型id
     * @return （若已存在则返回所有，不进行初始化，若不存在则初始化默认组）
     */
    @Override
    public List<DapDataModelGroup> initModelDefaultGroup(String modelId) {
        // check
        DapDataModel model = modelService.getByIdAsset(modelId, "当前模型不存在！！");
        // 判断是否为已初始化模型分组，若存在是否删除历史分组
        List<DapDataModelGroup> groups = findByModelIdOrderByRankAndCreateTime(model.getId());
        if (!groups.isEmpty()) {
            // 只要默认组
            return groups.stream().filter(d -> StringUtils.isBlank(d.getPid()) && Boolean.TRUE.equals(d.getDefaultGroup())).collect(Collectors.toList());
        }
        Tuple2<Boolean, DapSystemInfo> tuple2 = modelService.isDwSystemByModelId(modelId);
        BmDefaultGroup groupsHandler = new BmDefaultGroup(modelId, tuple2.getT1(), tuple2.getT2().getId());
        groups = groupsHandler.getGroups();
        if (groups != null && !groups.isEmpty()) {
            boolean saveBatch = saveBatch(groups);
            log.info("初始化模型{}的分组信息，size={}，save={}", modelId, groups.size(), saveBatch);
            return groups;
        }
        return Collections.emptyList();
    }

    /**
     * 根据模型id获取分组信息
     *
     * @param modelId 模型id
     * @return 根据模型id获取分组信息
     */
    @Override
    public List<DapDataModelGroup> findByModelIdOrderByRankAndCreateTime(String modelId) {
        LambdaQueryWrapper<DapDataModelGroup> where = bmLambdaQuery()
                .eq(true, DapDataModelGroup::getModelId, modelId)
                .orderByAsc(DapDataModelGroup::getGroupRank).orderByAsc(DapDataModelGroup::getCreateTime);
        return list(where);
    }

    /**
     * 根据模型id+组信息
     * 判断groups中的是否存在表的情况
     *
     * @param modelId 模型id
     * @param groups  分组信息
     * @return true 存在，false都不存在
     */
    private boolean isAnyMatchHadTable(String modelId, List<DapDataModelGroup> groups) {
        // 是否针对模型最新表数据
        boolean dataNewest = true;
        boolean anyMatchHadTable = groups.stream().anyMatch(g -> {
            long tables = tableService.countByGroupPidAndGroupIdAndDataNewest(g.getPid(), g.getId(), modelId, dataNewest);
            return tables != 0;
        });
        return anyMatchHadTable;
    }

    /**
     * 新增组
     *
     * @param modelId   模型id
     * @param groupPid  默认组id 组1
     * @param groupName 新增的组 组2名称
     * @param groupRank 组排序号
     * @return 新组带id
     */
    @Override
    @Transactional
    public DapDataModelGroup addGroupByGroupPid(String modelId, String groupPid, String groupName,
                                                int groupRank) {
        // 模型为数仓、非数仓
        Tuple2<Boolean, DapSystemInfo> dwAndSysInfo = modelService.isDwSystemByModelId(modelId);
        Boolean dwSystem = dwAndSysInfo.getT1();
        // 非数仓约束
        if (Boolean.FALSE.equals(dwSystem)) {
            log.warn("非数仓系统只允许创建一层分组，需要指定groupPid为空/null!!");
            BmAssetUtils.isTrue(StringUtils.isBlank(groupPid), "非数仓系统只允许创建一层分组");
        } else {
            // 数仓约束，只允许创建两层分组关系
            // 1层无需约束,2+层处理
            if (StringUtils.isNotBlank(groupPid)) {
                DapDataModelGroup pGroup = getByIdAsset(groupPid, "父组不存在，请重试！");
                log.warn("父类组id={}不属于模型id={}不可新增组信息", groupPid, modelId);
                BmAssetUtils.isTrue(modelId.equals(pGroup.getModelId()), "父类组不属于当前模型，不可新增组信息");
                // 判断新增节点是否在两层分组内
                // 父组为ODS等，不存在更上层
                BmAssetUtils.isTrue(StringUtils.isBlank(pGroup.getPid()), "数仓系统最多支持两层分组");
            }
        }
        // 判断目标组名同级是否已存在 判断是否pid组下(pid可能为null)已存在该组名，若已存在则返回错误警告
        boolean existGroupName = existGroupNameByPidAndName(modelId, groupPid, groupName);
        BmAssetUtils.isFalse(existGroupName, "同层组下已存在新增组名");
        // 写入
        DapDataModelGroup newGroup = new DapDataModelGroup();
        newGroup.setPid(groupPid);
        newGroup.setDefaultGroup(Boolean.FALSE);
        newGroup.setName(groupName);
        newGroup.setGroupRank(groupRank);
        newGroup.setModelId(modelId);
        newGroup.setSystemId(dwAndSysInfo.getT2().getId());
        boolean save = save(newGroup);
        log.info("父类组{}#id={}，保存新子组{}#id={}，情况：{}", groupName, groupPid,
                newGroup.getName(), newGroup.getId(), save);
        // 增加清除已缓存
        GROUP_CACHE_BY_NAME_AND_MODEL.invalidate(getGroupNameAndModelId(newGroup.getName(), newGroup.getModelId()));
        return newGroup;
    }

    @Deprecated
    private DapDataModelGroup findGroupByPid(String pid) {
        DapDataModelGroup preGroup = getByIdAsset(pid);
        return preGroup;
    }

    /**
     * 判断是否已存在组
     *
     * @param pid       父id
     * @param groupName 组名
     * @return true 已存在 false 不
     */
    private boolean existGroupNameByPidAndName(String modelId, String pid, String groupName) {
        LambdaQueryWrapper<DapDataModelGroup> eq = bmLambdaQuery()
                .eq(DapDataModelGroup::getModelId, modelId)
                .eq(isNotBlank(pid), DapDataModelGroup::getPid, pid)
                .eq(DapDataModelGroup::getName, groupName);
        int count = count(eq);
        return count >= 1;
    }

    /**
     * 编辑组
     *
     * @param id        组id
     * @param groupName 组名
     * @param groupRank 修改后对象
     * @return 返回修改后的对象
     */
    @Override
    public DapDataModelGroup editGroupById(String id, String groupName, int groupRank) {
        DapDataModelGroup group = getByIdAsset(id, "组信息不存在，请刷新");
        checkModelExist(group);
        // 默认组不可操作
        BmAssetUtils.isFalse(Boolean.TRUE.equals(group.getDefaultGroup()), "默认组不可编辑");
        // 如果旧和新没变忽略，反之，如下
        if (!StringUtils.equals(groupName, group.getName())) {
            // 判断目标组名同级是否已存在 判断是否pid组下(pid可能为null)已存在该组名，若已存在则返回错误警告
            boolean existGroupName = existGroupNameByPidAndName(group.getModelId(), group.getPid(), groupName);
            BmAssetUtils.isFalse(existGroupName, "同层组下已存在新增组名");
        }
        // action
        group.setName(groupName);
        group.setGroupRank(groupRank);
        boolean update = updateById(group);
        log.info("模型id={},修改组名为为：{}, status: {}", group.getModelId(), groupName, update);
        // 增加清除已缓存
        GROUP_CACHE_BY_NAME_AND_MODEL.invalidate(getGroupNameAndModelId(group.getName(), group.getModelId()));
        return group;
    }

    private void checkModelExist(DapDataModelGroup group) {
        modelService.getByIdAsset(group.getModelId(), "模型已被删除，不可操作");
    }

    /**
     * 组id
     *
     * @param id 删除组
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delGroupById(String id) {
        DapDataModelGroup group = getByIdAsset(id, "组信息不存在，请刷新");
        checkModelExist(group);
        BmAssetUtils.isFalse(Boolean.TRUE.equals(group.getDefaultGroup()), "默认组不可删除");
        boolean anyMatchHadTable = isAnyMatchHadTable(group.getModelId(), Collections.singletonList(group));
        log.warn("分组{}#id={}下存在表信息", group.getName(), group.getId());
        BmAssetUtils.isFalse(anyMatchHadTable, "分组下存在关联表，不可删除");
        removeByIdsAsset(true, id);
        // 增加清除已被删除的缓存
        GROUP_CACHE_BY_NAME_AND_MODEL.invalidate(getGroupNameAndModelId(group.getName(), group.getModelId()));
    }

    /**
     * 不带有公共模型数据
     *
     * @param modelId           模型id
     * @param keyword           keyword
     * @param nameWithTableSize 节点name是否需要携带表数据量
     * @return 模型设计左树
     */
    @Override
    public ModelLeftGroupOut modelLeftGroup(String modelId, String keyword, boolean nameWithTableSize) {
        if (nameWithTableSize) {
            return modelLeftGroupOutWithNameTableSize(modelId, keyword);
        } else {
            return modelLeftGroupOut0(modelId, false, false, keyword);
        }
    }

    /**
     * 组名携带表数据量
     */
    private ModelLeftGroupOut modelLeftGroupOutWithNameTableSize(String modelId, String keyword) {
        // 增加需要节点表数据量 needGroupTables=true
        ModelLeftGroupOut modelLeftGroupOut = modelLeftGroupOut0(modelId, true, false, keyword);
        // 只需要表数据量，无需表数据 处理两层
        List<DapDataModelGroupDto> groupTreeVos = modelLeftGroupOut.getCurrentModelGroups().getGroupTreeVos();
        for (DapDataModelGroupDto g : groupTreeVos) {
            g.setTables(null);
            int nextNodeSum = 0;
            if (g.getNextNodes() != null) {
                for (DapDataModelGroupDto g2 : g.getNextNodes()) {
                    g2.setTables(null);
                    groupNameWithTableSize(g2, 0);
                    nextNodeSum += g2.getTableSize();
                }
            }
            groupNameWithTableSize(g, nextNodeSum);
        }
        return modelLeftGroupOut;
    }

    private void groupNameWithTableSize(DapDataModelGroupDto g, int nextNodeSum) {
        int groupTableSize = Optional.ofNullable(g.getTableSize()).map(s -> s + nextNodeSum).orElse(nextNodeSum);
        String name = Optional.ofNullable(g.getName()).map(s -> s.concat("(" + groupTableSize + ")")).orElse("");
        g.setName(name);
    }

    /**
     * 模型id
     * 1.获取是否为数仓模型
     * 2.进行分组逻辑处理
     * 3.不带/带有公共模型数据
     *
     * @param modelId         模型id
     * @param keyword         keyword
     * @param needCommonModel 是否需要获取公共模型的结构数据
     * @param needGroupTables 是否需要获取每个分组下的表信息
     * @return modelLeftGroupOut
     */
    private ModelLeftGroupOut modelLeftGroupOut0(String modelId, boolean needGroupTables, boolean needCommonModel, String keyword) {
        DapDataModel currentModel = modelService.getByIdAsset(modelId, "获取模型信息失败");
        boolean isDwSystem = modelService.isDwSystemByModelId(modelId).getT1();
        ModelLeftGroupOut modelLeftGroupOut = new ModelLeftGroupOut();
        // 当前模型
        ModelGroupOut modelGroupOut = new ModelGroupOut();
        modelGroupOut.setModelId(modelId);
        modelGroupOut.setDwSystem(isDwSystem);
        // 当前模型都不是共享模型（共享模型只是虚拟出来的模型）
        modelGroupOut.setCommonModel(false);
        modelGroupOut.setCurrentModel(true);
        DapSystemDatasource curDs = systemModuleFacade.getDbTypeByDataSourceId(currentModel.getDataSourceId());
        modelGroupOut.setDbType(curDs.getDatabaseType().getCode());
        modelGroupOut.setModelName(currentModel.getName());
        // 根据模型id获取分组列表
        List<DapDataModelGroupDto> modelGroupDtos = getDapDataModelGroupDtos(modelId);
        List<DapDataModelGroupDto> trees = TreeUtils.createTrees(modelGroupDtos, "id", "pid", "nextNodes", "level", 0);
        log.info("获取到模型的分组树形结构完成");
        if (needGroupTables) {
            log.info("获取到模型的分组树形结构完成，查询当前模型下的表信息，{}", modelId);
            setLeafNodeTable(trees);
        }
        modelGroupOut.setGroupTreeVos(trees);
        log.info("查询当前模型完成，{}", modelId);
        modelLeftGroupOut.setCurrentModelGroups(modelGroupOut);
        // 获取公共模型
        if (needCommonModel) {
            List<ModelGroupOut> commonGroups = getCommonModelGroupOuts(modelId, isDwSystem, Collections.singleton(modelId));
            // 增加是否显示字段show=true显示，反之不用显示
            analyseModelGroupIsShows(commonGroups);
            modelLeftGroupOut.setCommonModelGroups(commonGroups);
            log.info("当前模型的公共模型信息：" + commonGroups.size());
        }
        // 增加keyword查询能力，目前树高度最大为2直接遍历两次，对当前+公共进行过滤
        if (StringUtils.isNotBlank(keyword)) {
            log.info("针对关键字进行查询当前模型下的表信息，{}", modelId);
            ModelGroupOut currentModelGroups = modelLeftGroupOut.getCurrentModelGroups();
            // 处理当前模型
            filterModelGroupTableByKeyword(currentModelGroups, keyword);
            // 处理公共模型
            List<ModelGroupOut> commonModelGroups = modelLeftGroupOut.getCommonModelGroups();
            for (ModelGroupOut commonModelGroup : commonModelGroups) {
                filterModelGroupTableByKeyword(commonModelGroup, keyword);
            }
        }
        log.debug("当前模型的分组：" + modelLeftGroupOut.getCurrentModelGroups().getModelName());
        return modelLeftGroupOut;
    }

    private void analyseModelGroupIsShows(List<ModelGroupOut> commonGroups) {
        for (ModelGroupOut commonGroup : commonGroups) {
            analyseModelGroupIsShow(commonGroup);
        }
    }

    private void analyseModelGroupIsShow(ModelGroupOut commonGroup) {
        List<DapDataModelGroupDto> groupTreeVos = commonGroup.getGroupTreeVos();
        for (DapDataModelGroupDto group1 : groupTreeVos) {
            List<DapModelGroupTableDto> tables = group1.getTables();
            // 过滤组1
            final boolean group1HitKeyword = !tables.isEmpty();
            if (!group1HitKeyword) {
                group1.setShow(Boolean.FALSE);
            }
            List<DapDataModelGroupDto> groupTreeVos2 = group1.getNextNodes();
            // 只有有一个就调整g1为true
            if (groupTreeVos2 != null && !groupTreeVos2.isEmpty()) {
                for (DapDataModelGroupDto group2 : groupTreeVos2) {
                    List<DapModelGroupTableDto> tables2 = group2.getTables();
                    boolean group2HitKeyword = !tables2.isEmpty();
                    if (group2HitKeyword && !group1HitKeyword) {
                        group1.setShow(Boolean.TRUE);
                    }
                    if (!group2HitKeyword) {
                        group2.setShow(Boolean.FALSE);
                    }
                }
            }
        }
    }

    /**
     * 通过当前模型id获取对应系统下的公共模型
     *
     * @param modelId       当前模型id
     * @param outOfModelIds 获取到的公共模想信息中排除哪些模型id
     * @param isDwSystem    是否为数仓系统
     * @return 通过当前模型id获取对应系统下的公共模型
     */
    private List<ModelGroupOut> getCommonModelGroupOuts(String modelId, boolean isDwSystem,
                                                        final Set<String> outOfModelIds) {
        // 其他模型
        List<DapDataModel> commonModels = modelService.findCommonModelsByModelId(modelId);
        // 如果自己是公共模型，不过滤掉自己;对每个模型获取各自的组信息
        List<ModelGroupOut> commonGroups = commonModels.stream()
                .filter(d -> !outOfModelIds.contains(d.getId()))
                .map(dapDataModel -> {
                    String dataSourceId = dapDataModel.getDataSourceId();
                    ModelGroupOut groupOut = new ModelGroupOut();
                    DapSystemDatasource commDs = systemModuleFacade.getDbTypeByDataSourceId(dataSourceId);
                    groupOut.setDbType(commDs.getDatabaseType().getCode());
                    groupOut.setCommonModel(true);
                    groupOut.setModelId(dapDataModel.getId());
                    groupOut.setCurrentModel(StringUtils.equals(dapDataModel.getId(), modelId));
                    groupOut.setDwSystem(isDwSystem);
                    groupOut.setModelName(dapDataModel.getName());
                    List<DapDataModelGroupDto> dapDataModelGroupDtos = getDapDataModelGroupDtos(dapDataModel.getId());
                    List<DapDataModelGroupDto> groupTreeDtos = TreeUtils.createTrees(dapDataModelGroupDtos, "id", "pid", "nextNodes", "level", 0);
                    setLeafNodeTableCommonModel(groupTreeDtos);
                    groupOut.setGroupTreeVos(groupTreeDtos);
                    return groupOut;
                }).collect(Collectors.toList());
        log.info("获取到公共模型的分组信息+表信息完成，{}", commonGroups.size());
        return commonGroups;
    }

    public void setLeafNodeTableCommonModel(List<DapDataModelGroupDto> groupTreeDtos) {
        setLeafNodeTable(groupTreeDtos, true);
    }

    public void setLeafNodeTable(List<DapDataModelGroupDto> trees) {
        setLeafNodeTable(trees, false);
    }

    /**
     * 查询节点的表进行过滤
     */
    private void filterModelGroupTableByKeyword(ModelGroupOut currentModelGroups, String keyword) {
        List<DapDataModelGroupDto> groupTreeVos = currentModelGroups.getGroupTreeVos();
        Predicate<DapModelGroupTableDto> namePredicate = d -> containsIgnoreCase(d.getName(), keyword);
        namePredicate = namePredicate.or(d -> containsIgnoreCase(d.getEngName(), keyword));
        for (DapDataModelGroupDto group1 : groupTreeVos) {
            List<DapModelGroupTableDto> tables = group1.getTables();
            // 过滤组1
            List<DapModelGroupTableDto> hits = tables.stream().filter(namePredicate).collect(Collectors.toList());
            group1.setTables(hits);
            final boolean group1HitKeyword = !hits.isEmpty();
            if (!group1HitKeyword) {
                group1.setShow(Boolean.FALSE);
            }
            List<DapDataModelGroupDto> groupTreeVos2 = group1.getNextNodes();
            // 只有有一个就调整g1为true
            if (groupTreeVos2 != null && !groupTreeVos2.isEmpty()) {
                for (DapDataModelGroupDto group2 : groupTreeVos2) {
                    List<DapModelGroupTableDto> tables2 = group2.getTables();
                    List<DapModelGroupTableDto> hits2 = tables2.stream().filter(namePredicate).collect(Collectors.toList());
                    group2.setTables(hits2);
                    boolean group2HitKeyword = !hits2.isEmpty();
                    if (group2HitKeyword && !group1HitKeyword) {
                        group1.setShow(Boolean.TRUE);
                    }
                    if (!group2HitKeyword) {
                        group2.setShow(Boolean.FALSE);
                    }
                }
            }
        }
    }

    /**
     * 模型id
     * 1.获取是否为数仓模型
     * 2.进行分组逻辑处理
     * 3.带有公共模型数据
     *
     * @param modelId 模型id
     * @param keyword ky
     * @return modelLeftGroupOut
     */
    @Override
    public ModelLeftGroupOut modelLeftGroupAndCommonModel(String modelId, String keyword) {
        return modelLeftGroupOut0(modelId, true, true, keyword);
    }

    /**
     * 根据pid获取子组
     *
     * @param modelId  模型id
     * @param groupPid 分组id
     * @return 组下的自足
     */
    @Override
    public List<DapDataModelGroup> findSonGroupsByModelIdAndRootGroupId(String modelId, String groupPid) {
        return list(bmLambdaQuery().eq(DapDataModelGroup::getPid, groupPid)
                .eq(DapDataModelGroup::getModelId, modelId));
    }

    /**
     * 分组获取当下的表数据
     * 分页
     *
     * @param searchVo a
     */
    @SuppressWarnings("rawtypes")
    @Override
    public IPage<GroupModelTableVo> pageModelTablesByGroupId(GroupTableSearchVo searchVo) {
        DapDataModelGroup cruGroup = getByIdAsset(searchVo.getGroupId(), "当前组不存在！！");
        return tableService.pageDapDataModelTable(searchVo, cruGroup)
                .convert(table -> {
                    // 处理表中的组信息
                    String group1 = "";
                    String group2 = "";
                    String pid = cruGroup.getPid();
                    if (StringUtils.isNotBlank(pid)) {
                        DapDataModelGroup groupByPid = findGroupByPid(pid);
                        if (groupByPid != null) {
                            group1 = groupByPid.getName();
                            group2 = cruGroup.getName();
                        } else {
                            log.warn("组id={}#pid={}已被删除！子节点还保留，请检查。", cruGroup.getId(), cruGroup.getPid());
                        }
                    } else {
                        group1 = cruGroup.getName();
                    }
                    ModelSimpleTableVo tableVo = new ModelSimpleTableVo(table);
                    return GroupModelTableVo.builder()
                            .group1(group1).group2(group2)
                            .table(tableVo).build();
                });
    }

    /**
     * 加入组
     *
     * @param tableIds 表ids
     * @param groupId  分组
     */
    @Override
    public void addGroupRelationToTables(List<String> tableIds, String groupId) {
        DapDataModelGroup currentGroup = getByIdAsset(groupId, "当前组不存在！！");
        BmAssetUtils.isFalse(tableIds == null || tableIds.isEmpty(), "请选择相关表，加入组！！");
        // 如果所选的表不是当前组对应的模型，不可加入
        Collection<DapDataModelTable> tables = tableService.listByIds(tableIds);
        boolean anyEqModelId = tables.stream().anyMatch(t -> StringUtils.equals(t.getDataModelId(), currentGroup.getModelId()));
        if (!anyEqModelId) {
            log.warn("发现所选的表ids={}中的模型id与分组id={}对应的模型id不一样！！", tableIds, groupId);
        }
        BmAssetUtils.isTrue(anyEqModelId, "所选的表与组不在同一个模型下！！");
        tableService.addGroupRelationToTables(tableIds, currentGroup.getPid(), currentGroup.getId());
    }

    /**
     * 获取组信息+父组信息
     *
     * @param groupId 模型的组id
     */
    @Override
    public ModelGroupDto getCurrentGroupAndPreGroup(String groupId) {
        DapDataModelGroup currGroup = getByIdAsset(groupId, "当前组不存在！");
        String modelId = currGroup.getModelId();
        Tuple2<Boolean, DapSystemInfo> tuple2 = modelService.isDwSystemByModelId(modelId);
        Boolean dwSystem = tuple2.getT1();
        if (currGroup.getDefaultGroup() || StringUtils.isBlank(currGroup.getPid())) {
            return ModelGroupDto.builder().dwSystem(dwSystem).currGroup(currGroup).build();
        }
        String pid = currGroup.getPid();
        DapDataModelGroup preGroup = findGroupByPid(pid);
        return ModelGroupDto.builder().dwSystem(dwSystem).currGroup(currGroup).preGroup(preGroup).build();
    }

    /**
     * 删除模型下的分组数据
     *
     * @param isLogicDel true逻辑删除
     * @param modelId    模型id
     */
    @Override
    public void deleteByModelId(boolean isLogicDel, String modelId) {
        if (modelId == null) {
            return;
        }
        boolean result = false;
        if (isLogicDel) {
            LambdaUpdateWrapper<DapDataModelGroup> updateWrapper = bmLambdaUpdate().eq(DapDataModelGroup::getModelId, modelId)
                    .set(DapDataModelGroup::getBmStatus, BmStatus.DELETE);
            result = update(updateWrapper);
        } else {
            LambdaQueryWrapper<DapDataModelGroup> eq = bmLambdaQuery().eq(DapDataModelGroup::getModelId, modelId);
            result = remove(eq);
        }
        log.info("{}清除模型{}下的分组，情况：{}", isLogicDel ? "逻辑" : "物理", modelId, result);
    }

    private String getGroupNameAndModelId(String g, String mId) {
        return g + GROUP_AND_MODEL_SPLIT_CHAR + mId;
    }

    /**
     * 根据模型id+组名称获取id
     * cache
     *
     * @param groupName z
     * @param modelId   z
     */
    @Override
    public List<DapDataModelGroup> findGroupByGroupNameAndModelIdCache(String groupName, String modelId) {
        if (groupName != null) {
            final String groupAndModelId = getGroupNameAndModelId(groupName, modelId);
            try {
                return GROUP_CACHE_BY_NAME_AND_MODEL.get(groupAndModelId, () -> {
                    LambdaQueryWrapper<DapDataModelGroup> eq = bmLambdaQuery().eq(DapDataModelGroup::getName, groupName).eq(DapDataModelGroup::getModelId, modelId);
                    return list(eq);
                });
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        return Collections.emptyList();
    }

    @Override
    public void checkGroupAndModel(String groupId, String modelId) {
        BmAssetUtils.isTrue(StringUtils.isNotBlank(groupId), "组id不可为空");
        BmAssetUtils.isTrue(StringUtils.isNotBlank(modelId), "模型id不可为空");
        DapDataModelGroup group = getByIdAsset(groupId, "组信息不存在！！");
        DapDataModel model = modelService.getByIdAsset(modelId, "模型信息不存在！！");
        BmAssetUtils.isTrue(StringUtils.equals(group.getModelId(), model.getId()), "模型不存在当前组！！");
    }

    @Override
    public void batchDeleteTables(List<String> tableIds, String groupId, String modelId) {
        DapDataModel model = modelService.getModelById(modelId);
        DapSystemDatasource ds = systemModuleFacade.getDbTypeByDataSourceId(model.getDataSourceId());
        final DatabaseType databaseType = ds.getDatabaseType();
        for (String tableId : tableIds) {
            Boolean del = tableService.deleteTableAll(tableId, databaseType);
            log.debug("删除表{}情况{}", tableId, del);
        }
    }

    /**
     * 根据模型id获取分组信息
     *
     * @param modelId modelId
     * @return 根据模型id获取分组信息
     */
    private List<DapDataModelGroupDto> getDapDataModelGroupDtos(String modelId) {
        List<DapDataModelGroup> groupsFromModel = findByModelIdOrderByRankAndCreateTime(modelId);
        List<DapDataModelGroupDto> modelGroupDtos = groupsFromModel.stream().filter(Objects::nonNull).map(g -> {
            DapDataModelGroupDto groupTreeDto = new DapDataModelGroupDto();
            BeanUtils.copyProperties(g, groupTreeDto);
            return groupTreeDto;
        }).collect(Collectors.toList());
        return modelGroupDtos;
    }

    private void setLeafNodeTable(List<DapDataModelGroupDto> trees, boolean commonModel) {
        // 仅针对第二层进行set表列表信息 2021年2月20日15:58:54调整为每个组节点都去获取自己的表
        for (DapDataModelGroupDto tree : trees) {
            selectSonNode(tree, commonModel);
            List<DapDataModelGroupDto> nextNodes = tree.getNextNodes();
            if (nextNodes != null) {
                for (DapDataModelGroupDto nextNode : nextNodes) {
                    selectSonNode(nextNode, commonModel);
                }
            }
        }
    }

    /**
     * 根据所给的组对象，的pid&id获取对应的表信息，并写入组对象中
     *
     * @param group 租对象
     */
    public void selectSonNode(DapDataModelGroupDto group, boolean commonModel) {
        List<DapModelGroupTableDto> tableDtos = tableService.findNewestTablesByGroupPidAndGroupId(group.getPid(), group.getId(),
                group.getModelId()).stream().map(DapModelGroupTableDto::new).collect(Collectors.toList());
        if (commonModel) {
            List<DapModelGroupTableDto> shareTables = tableDtos.stream().filter(t -> Boolean.TRUE.equals(t.getShareTable())).collect(Collectors.toList());
            group.setTables(shareTables);
            group.setTableSize(shareTables.size());
        } else {
            group.setTables(tableDtos);
            group.setTableSize(tableDtos.size());
        }
    }

    public enum NotDwSysGroup implements IGroup {
        /**
         * 1
         */
        DEFAULT(1, "默认组");
        private final int code;
        private final String name;

        NotDwSysGroup(int code, String name) {
            this.code = code;
            this.name = name;
        }


        @Override
        public int getCode() {
            return code;
        }

        @Override
        public String getName() {
            return name;
        }


    }

    public enum DwGroup implements IGroup {
        ODS(1, "ODS"),
        DWD(2, "DWD"),
        DIM(3, "DIM"),
        DWS(4, "DWS"),
        ADS(5, "ADS");
        private final int code;
        private final String name;

        DwGroup(int code, String name) {
            this.code = code;
            this.name = name;
        }


        @Override
        public int getCode() {
            return code;
        }

        @Override
        public String getName() {
            return name;
        }

    }

    /**
     * 分组初始化信息接口
     *
     * @author Jarod.Kong
     */
    public interface IGroup {
        String getName();

        int getCode();

    }

    public static class BmDefaultGroup {
        private final String modelId;
        private final boolean dwSystem;
        private final String systemId;

        public BmDefaultGroup(String modelId, boolean dwSystem, String systemId) {
            this.modelId = modelId;
            this.dwSystem = dwSystem;
            this.systemId = systemId;
        }

        public List<DapDataModelGroup> getGroups() {
            IGroup[] values = getValues();
            return Arrays.stream(values).map(val -> {
                DapDataModelGroupDto modelGroup = new DapDataModelGroupDto();
//                modelGroup.setDwSystem(dwSystem);
                modelGroup.setDefaultGroup(Boolean.TRUE);
                modelGroup.setModelId(modelId);
                modelGroup.setGroupRank(val.getCode());
                modelGroup.setPid(null);
                modelGroup.setSystemId(systemId);
                modelGroup.setName(val.getName());
                return modelGroup;
            }).collect(Collectors.toList());
        }


        public IGroup[] getValues() {
            if (dwSystem) {
                return DwGroup.values();
            }
            return NotDwSysGroup.values();
        }


        public boolean isDwSystem() {
            return dwSystem;
        }

        public String getModelId() {
            return modelId;
        }
    }
}
